package com.blue.common.listener;

import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import cn.idev.excel.exception.ExcelDataConvertException;
import cn.idev.excel.metadata.CellExtra;
import cn.idev.excel.metadata.data.ReadCellData;
import com.blue.common.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class ExcelDataImportListener<T> extends AnalysisEventListener<T> {
    private final Class<T> clazz;

    private final List<T> result;

    public ExcelDataImportListener(Class<T> clazz, List<T> result) {
        this.clazz = clazz;
        this.result = result;
    }

    @Override
    public void onException(Exception e, AnalysisContext analysisContext) {
        if (e instanceof ExcelDataConvertException excelDataConvertException) {
            String exceptionMsg = "导入文件解析失败，第" + (excelDataConvertException.getRowIndex() + 1) + "行,第" + (excelDataConvertException.getColumnIndex() + 1) + "列数据解析异常，数据为"
                    + excelDataConvertException.getCellData().getStringValue() + ",失败原因：" + e.getMessage();
            throw new CustomException(exceptionMsg);
        }
    }


    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> map, AnalysisContext analysisContext) {
        // 获取数据实体的字段列表
        Field[] fields = clazz.getDeclaredFields();
        // 导入模板对象表头列表
        List<String> tempHeadList = Arrays.stream(fields)
                // 只选取有ExcelProperty注解的字段
                .filter(field -> Objects.nonNull(field.getAnnotation(ExcelProperty.class)))
                // 通过ExcelProperty注解获取规定的表头名列表
                .map(field -> CollectionUtils.firstElement(Arrays.asList(field.getAnnotation(ExcelProperty.class).value())))
                .collect(Collectors.toList());
        // 导入文件表头列表
        List<String> fileHeadList = map.values().stream().map(ReadCellData::getStringValue).toList();
        // 判断用户导入表格的标题头是否完全包含模板的标题头
        if (!new HashSet<>(fileHeadList).containsAll(tempHeadList)) {
            tempHeadList.removeAll(fileHeadList);
            throw new CustomException(String.format("Excel文件读取失败，失败原因: 导入文件与系统模板不匹配，缺少标题头[%s]", String.join(", ", tempHeadList)));
        }
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        result.add(data);
    }

    @Override
    public void extra(CellExtra cellExtra, AnalysisContext analysisContext) {

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}
